﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.UI;
using System.Collections;
using System.Xml.Linq;
using System.ComponentModel;
using System.Linq.Expressions;

namespace Doo.WebUI.SiteBuilder
{
    /// <summary>
    /// 挂件提供者
    /// </summary>
    public abstract class WidgetProviderBase
    {
        /// <summary>
        /// 获取挂件配置
        /// </summary>
        /// <param name="configID">配置名</param>
        /// <returns></returns>
        /// <remarks>例如每个页面有各自的配置</remarks>
        public abstract WidgetConfig GetWidgetConfig(string configID);

        /// <summary>
        /// create control
        /// </summary>
        /// <param name="config"></param>
        /// <returns></returns>
        public abstract Control CreateWidgetControl(WidgetConfig config);
    }

    public class WidgetProvider : WidgetProviderBase
    {
        public Page OwnPage { get; set; }

        public Func<Control, Control> ControlCreated;

        public Dictionary<string, WidgetConfig> WidgetConfigs = new Dictionary<string, WidgetConfig>();

        public override Control CreateWidgetControl(WidgetConfig config)
        {
            Type t = config.Def.WidgetType;
            Control ctl = null;
            if (t != null && t.IsChildOf(typeof(WidgetControl)))
            {
                ctl = Activator.CreateInstance(t) as Control;
            }
            else if (config.Def.WidgetAscxType != null)
            {
                ctl = Activator.CreateInstance(config.Def.WidgetAscxType) as Control;
                t = ctl.GetType();
            }
            else if (config.Def.WidgetAscxPath != null)
            {
                ctl = OwnPage.LoadControl(config.Def.WidgetAscxPath);
                t = ctl.GetType();
            }
            else
            {
                return null;
            }

            #region 设置属性
            if (config.Properties != null)
            {
                Delegate setDelegate = config.GetPropertiesSet(ctl.GetType());
                setDelegate.DynamicInvoke(ctl);

                /*
                foreach (var entry in config.Properties)
                {
                    var p = t.GetProperty(entry.Key);
                    p.SetValue(ctl, entry.Value, null);
                }
                 */
            }
            #endregion

            #region 设置内部控件

            if (config.InnerWidgets != null)
            {
                foreach (var entry in config.InnerWidgets)
                {
                    ControlCollection cc = t.GetProperty(entry.Key).GetValue(ctl, null) as ControlCollection;
                    foreach (var cf in entry.Value)
                    {
                        cc.Add(CreateWidgetControl(cf));
                    }
                }
            }

            #endregion

            if (ControlCreated != null) ctl = ControlCreated(ctl);
            return ctl;
        }

        public override WidgetConfig GetWidgetConfig(string configID)
        {
            return WidgetConfigs[configID];
        }
    }

    /// <summary>
    /// 一个基于XML配置文件集的挂件提供者
    /// </summary>
    public class XMLWidgetProvider : WidgetProvider
    {
        /// <summary>
        /// 控件定义文件
        /// </summary>
        public string ControlDefFile { get; set; }

        /// <summary>
        /// The Last Must Be '/'
        /// </summary>
        public string SiteRootPath { get; set; }

        Dictionary<string, WidgetDef> cdcache;

        Dictionary<string, WidgetConfig> wcache = new Dictionary<string, WidgetConfig>();

        void InitControlDef()
        {
            XDocument xd = XDocument.Load(ControlDefFile);
            var page = xd.Element("Widgets");
            var ws = page.Elements();
            cdcache = new Dictionary<string, WidgetDef>();
            foreach (var d in ws)
            {
                WidgetDef wd = new WidgetDef();
                //1.Ascx
                var ascx = d.Attribute("AscxPath");
                if (ascx != null && !string.IsNullOrEmpty(ascx.Value)) wd.WidgetAscxPath = ascx.Value;
                //2.Compiled Ascx Type
                var ascxtype = d.Attribute("AscxType");
                if (ascxtype != null && !string.IsNullOrEmpty(ascxtype.Value))
                {
                    wd.WidgetAscxType = Type.GetType(ascxtype.Value);
                }
                //3.Control Type
                var type = d.Attribute("Type");
                if (type != null && !string.IsNullOrEmpty(type.Value))
                {
                    wd.WidgetAscxType = Type.GetType(type.Value);
                }
                //4.WidgetID
                cdcache[d.Name.LocalName] = wd;
            }
        }

        WidgetConfig LoadConfigFromXElement(XElement element)
        {
            //1.get def
            string tagname = element.Name.LocalName;
            WidgetConfig wf = new WidgetConfig();
            wf.Def = cdcache[tagname];
            //2.props
            if (element.HasAttributes)
            {
                var attrs = element.Attributes();
                Dictionary<string, object> wp = new Dictionary<string, object>();
                foreach (var at in attrs)
                {
                    string v = at.Value;
                    wp[at.Name.LocalName] = v;
                }
                wf.Properties = wp;
            }
            //3.inner
            if (element.HasElements)
            {
                var innereprops = element.Elements();
                Dictionary<string, WidgetConfig[]> inner = new Dictionary<string, WidgetConfig[]>();
                foreach (var p in innereprops)
                {
                    var wds = p.Elements();
                    List<WidgetConfig> innercf = new List<WidgetConfig>();
                    foreach (var it in wds)
                    {
                        innercf.Add(LoadConfigFromXElement(it));
                    }
                    inner[p.Name.LocalName] = innercf.ToArray();
                }
                wf.InnerWidgets = inner;
            }

            return wf;
        }

        public override WidgetConfig GetWidgetConfig(string configID)
        {
            if (cdcache == null) InitControlDef();
            WidgetConfig cf;
            if (!wcache.TryGetValue(configID, out cf))
            {
                cf = new WidgetConfig();
                string file = SiteRootPath + configID;
                XDocument xd = XDocument.Load(file);
                var page = xd.Root;
                //1.Auth
                //TO DO
                //2.Body
                var body = page.Element("Body");
                //特殊Root-Panel
                cf = new WidgetConfig { Def = new WidgetDef { WidgetType = typeof(WidgetPanel) } };
                List<WidgetConfig> inn = new List<WidgetConfig>();
                var wds = body.Elements();
                foreach (var it in wds)
                {
                    inn.Add(LoadConfigFromXElement(it));
                }
                cf.InnerWidgets = new Dictionary<string, WidgetConfig[]> { { "Widgets", inn.ToArray() } };

                wcache[configID] = cf;
            }
            return cf;
        }
    }

    public class WidgetDef
    {
        /// <summary>
        /// 挂件的类型
        /// </summary>
        public Type WidgetType { get; set; }

        /// <summary>
        /// Compiled Ascx Type
        /// Use WidgetAscxPath If not compiled
        /// </summary>
        public Type WidgetAscxType { get; set; }

        /// <summary>
        /// Use this if Widget is UserControl
        /// </summary>
        public string WidgetAscxPath { get; set; }


    }

    /// <summary>
    /// 挂件配置
    /// </summary>
    public class WidgetConfig
    {
        /// <summary>
        /// 定义
        /// </summary>
        public WidgetDef Def { get; set; }

        /// <summary>
        /// 挂件属性
        /// </summary>
        public Dictionary<string, object> Properties { get; set; }

        private Delegate propertiesSet = null;

        public Delegate GetPropertiesSet(Type runtimeType)
        {
            if (propertiesSet != null) return propertiesSet;

            var type = runtimeType ?? Def.WidgetType;
            ParameterExpression param = ParameterExpression.Parameter(typeof(Control), "widget");
            Expression wparam = Expression.Convert(param, type);
            List<Delegate> delegateList = new List<Delegate>();

            foreach (var pentry in Properties)
            {
                var pinfo = type.GetProperty(pentry.Key);
                var ptype = pinfo.PropertyType;
                var setmethod = pinfo.GetSetMethod();
                //if (ptype.IsValueType)
                {
                    //如果是值类型 则直接赋值
                    object destvalue = Convert.ChangeType(pentry.Value, ptype);
                    Expression expValue = Expression.Constant(destvalue, ptype);
                    var setexp = Expression.Call(wparam, setmethod, expValue);
                    var final = Expression.Lambda(setexp, param);
                    delegateList.Add(final.Compile());
                }
            }
            propertiesSet = MulticastDelegate.Combine(delegateList.ToArray());
            return propertiesSet;
        }

        /// <summary>
        /// 内部挂件
        /// </summary>
        public Dictionary<string, WidgetConfig[]> InnerWidgets { get; set; }
    }

    public class PageConfig
    {
        public string Title { get; set; }

        public WidgetConfig[] Widgets { get; set; }
    }

    /// <summary>
    /// 站点根节点
    /// </summary>
    public class SiteRoot : SiteDir
    {

    }

    public class SiteNode
    { }

    /// <summary>
    /// 站点的节点
    /// </summary>
    public class SiteDir : SiteNode
    {
        public Dictionary<string, SiteNode> ChildNodes { get; set; }
    }

    public class PageLeaf : SiteNode
    {
        public PageConfig Page { get; set; }
    }
}
